package com.sgcc.pda.web.upload.manager;

import android.os.Environment;
import android.util.Log;

import com.sgcc.pda.baselibrary.utils.FileUtil;
import com.sgcc.pda.baselibrary.utils.LogUtil;
import com.sgcc.pda.baselibrary.utils.Md5Util;
import com.sgcc.pda.web.upload.asynctask.UploadAsyncTask;
import com.sgcc.pda.web.upload.entity.BlockUploadParams;
import com.sgcc.pda.web.upload.entity.BlockUploadTaskEvent;
import com.sgcc.pda.web.upload.listener.UploadListener;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * Created by cj on 2019/11/18.
 * 针对一整个文件
 * temp文件0失败1成功2链路失败
 */
public class UploadTask {

    private static final String TAG = "cj";
    private ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
    private long blockSize;
    //压缩后的zip文件
    private File file;
    private String fileName;
    private String savePath;
    private String md5;
    private UploadListener uploadListener;
    private int blockCount;
    private CountDownLatch latch;
    private volatile AtomicInteger successBlocks = new AtomicInteger(0);
    private volatile AtomicInteger retryTimes = new AtomicInteger(3);
    private volatile AtomicInteger finishBlocks = new AtomicInteger(0);
    private String tempFilePath;
    private File tempFile;
    private List<UploadAsyncTask> asyncTaskList = new ArrayList<>();
    //是否删除源文件
    private boolean isDelete;
    //源文件路径,也当作tag唯一标识
    private String origionFilePath;


    public UploadTask(File file, String savePath, long blockSize, String origionFilePath, boolean isDelete, UploadListener uploadListener) {
        this.file = file;
        this.savePath = savePath;
        this.uploadListener = uploadListener;
        this.blockSize = blockSize;
        this.isDelete = isDelete;
        this.origionFilePath = origionFilePath;

        fileName = file.getName();
        md5 = Md5Util.getMD5(file.getAbsolutePath());
        blockCount = getBlockCount(file.length(), blockSize);
        latch = new CountDownLatch(blockCount);
        tempFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + fileName + ".temp";
        tempFile = new File(tempFilePath);
    }

    public void startUpload() {
        if (!file.exists()) {
            uploadListener.result(false, "上传文件不存在");
            return;
        }
        finishBlocks.set(0);
        successBlocks.set(0);

        //先判断是否断点续传，如果无temp记录文件，则从零上传；如果已经上传过了，则解析temp文件，续传未成功的块。
        if (tempFile.exists()) {
            //解析续传
            try {
                RandomAccessFile tempAccessFile = new RandomAccessFile(tempFilePath, "rwd");
                long length = tempAccessFile.length();
                for (int i = 0; i < length; i++) {
                    tempAccessFile.seek(i);
                    int read = tempAccessFile.read();
                    if (read == 1) {
                        finishBlocks.incrementAndGet();
                        successBlocks.incrementAndGet();
                        //异常（所有都为1）
                        if (successBlocks.get() == blockCount) {
                            tempFile.delete();
                            startUpload();
                        }
                    } else {
                        //未上传
                        uploadOneBlock(file, savePath, i, blockCount, md5);
                    }
                }
                tempAccessFile.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                RandomAccessFile tempAccessFile = new RandomAccessFile(tempFilePath, "rwd");
                tempAccessFile.setLength(blockCount);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //从零上传
            for (int i = 0; i < blockCount; i++) {
                uploadOneBlock(file, savePath, i, blockCount, md5);
            }
        }
    }

    /**
     * 根据文件总长度，以及定义的每块长度，获取最后一块的长度
     *
     * @param totalFileSize 文件总长度
     * @param blockFileSize 每块长度
     * @return 最后一块的长度
     */
    private long getLastBlockFileSize(long totalFileSize, long blockFileSize) {
        if (totalFileSize <= 0 || blockFileSize <= 0) {
            return 0;
        }
        long lastBlockFileSize = totalFileSize % blockFileSize;
        return lastBlockFileSize == 0 ? blockFileSize : lastBlockFileSize;
    }

    /**
     * 根据文件总长度，每块文件长度 获取块数
     *
     * @param totalFileSize 文件总长度
     * @param blockFileSize 每块文件长度
     * @return int 总块数
     */
    private int getBlockCount(long totalFileSize, long blockFileSize) {
        // 若所传数据均不合法，则默认为一块
        if (totalFileSize <= 0 || blockFileSize <= 0 || totalFileSize <= blockFileSize) {
            return 1;
        }
        // 是否有余数
        boolean hasRemainder = (totalFileSize % blockFileSize) != 0;
        int blockSize = (int) (totalFileSize / blockFileSize);
        return hasRemainder ? blockSize + 1 : blockSize;
    }


    /**
     * 开启一块上传
     */
    private void uploadOneBlock(final File file, String savePath, int blockNo, final int blockCount, String md5) {

        byte[] buffer = getBlockData(file, blockNo, blockCount);
        String range = (blockNo * blockSize) + "-" + (blockNo * blockSize + buffer.length);
        BlockUploadParams params = new BlockUploadParams();
        params.setBlockSum(blockCount);
        params.setFileSize(file.length());
        params.setFileName(file.getName());
        params.setFileMd5(md5);
        params.setRangeKey(range);

        params.setCurrentBlockNo(blockNo + 1);

        UploadAsyncTask uploadTask = new UploadAsyncTask(savePath, buffer, params, new UploadAsyncTask.BlockListener() {
            @Override
            public void result(int currentBlockNo, boolean isSuccess) {
                //单块回调
                //如果没有链路失败，那么一定会触发总回调，所以单块回调就不需要处理了，
                //如果存在链路失败，那么总回调有可能不会触发。
                if (isSuccess) {

                    int i = successBlocks.incrementAndGet();
                    if (i <= blockCount) {
                        uploadListener.progress(i, blockCount, 2);
                    }

                }
                finishBlocks.incrementAndGet();
                LogUtil.e(TAG, "finishBlocks == " + finishBlocks + "  第" + currentBlockNo + "块结束");
                if (finishBlocks.get() == blockCount) {
                    //完了之后，判断success是否全部，不全就重试
                    LogUtil.e(TAG, "finish等于总块数，开始比较successBlocks");
                    if (successBlocks.get() == blockCount) {
                        //成功
                        EventBus.getDefault().post(new BlockUploadTaskEvent(getTag()));

                    } else {
                        //重试
                        if (retryTimes.decrementAndGet() >= 0) {
                            LogUtil.e(TAG, "重试次数还剩" + retryTimes.get() + "次");
                            startUpload();
                        } else {
                            LogUtil.e(TAG, "重试次数用完，返回总失败");

                            stopUpload();
                            uploadListener.result(false, "失败");
                            EventBus.getDefault().post(new BlockUploadTaskEvent(getTag()));

                        }
                    }
                }
            }

            @Override
            public void finish(boolean success, String msg) {

                //删除缓存文件
                deleteTempFile();
                //停止所有请求
                stopUpload();
                //删除zip文件
                FileUtil.deleteFile(file.getAbsolutePath());
                //删除源文件
                if (success && isDelete) {
                    boolean delete = FileUtil.deleteFolder(origionFilePath);
                    Log.e(TAG, "finish:源文件删除是否成功" + delete);
                }

                EventBus.getDefault().post(new BlockUploadTaskEvent(getTag()));
                try {
                    uploadListener.progress(blockCount, blockCount, 2);
                    uploadListener.result(success, msg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        uploadTask.executeOnExecutor(executor);
        asyncTaskList.add(uploadTask);
    }


    /**
     * 获取单块数据
     *
     * @param i          index下标
     * @param blockCount 总块数
     */
    private byte[] getBlockData(File file, int i, int blockCount) {

        byte[] buffer;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rwd");
            randomAccessFile.seek(i * blockSize);
            if (i == blockCount - 1) {
                long lastBlockFileSize = getLastBlockFileSize(file.length(), blockSize);
                buffer = new byte[(int) lastBlockFileSize];
                LogUtil.e(TAG, "最后一块 size: " + lastBlockFileSize + " range: " + (i * blockSize) + "-" + (i * blockSize + buffer.length));
            } else {
                buffer = new byte[(int) blockSize];
                LogUtil.e(TAG, "第" + (i + 1) + "块 size: " + blockSize + " range: " + (i * blockSize) + "-" + (i * blockSize + buffer.length));
            }
            randomAccessFile.read(buffer);
            randomAccessFile.close();
            return buffer;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }

    /**
     * 停止此文件的上传
     * 将所有的asynctask取消执行。
     */
    public void stopUpload() {
        for (int i = 0; i < asyncTaskList.size(); i++) {
            UploadAsyncTask asyncTask = asyncTaskList.get(i);
            asyncTask.cancel(true);
            asyncTask.cancelRequest();
        }
    }


    public void deleteTempFile() {
        FileUtil.deleteFile(tempFilePath);
    }

    public String getTag() {
        return origionFilePath;
    }

    public UploadListener getListener() {
        return uploadListener;
    }

    public String getZipFilePath() {
        return file.getAbsolutePath();
    }
}
